package cn.mw.cmdb.service.impl;

import cn.mw.cmdb.entity.PropertyInfo;
import cn.mw.cmdb.entity.PropertyType;
import cn.mw.cmdb.entity.PropertyTypeManage;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.view.ViewConvertContext;
import cn.mw.components.mongodb.entity.CriteriaOpsType;
import cn.mw.components.mongodb.entity.SearchParam;
import cn.mw.components.mongodb.entity.SubCondition;
import cn.mw.microMonitorCommon.api.user.MwUserApi;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public abstract class BaseSearchService<T> {

    protected abstract T genObject();

    protected Query genQuery(SearchParam param, List<Criteria> criterias) {
        T dataObject = genObject();
        Map<String, PropertyInfo> propertyInfos = CMDBTool.genSearchPropertyInfo(dataObject.getClass());
        return genQuery(propertyInfos, param, criterias);
    }

    protected Query genQuery(Map<String, PropertyInfo> propertyInfos, SearchParam param, List<Criteria> criterias) {
        return genQuery(propertyInfos, param, null, criterias);
    }

    protected Query genQuery(Map<String, PropertyInfo> propertyInfos, SearchParam param, String prefix
            , List<Criteria> criterias) {
        Query query = new Query();

        Criteria criteria = genCriteria(propertyInfos, param, prefix, criterias, null);
        if (null != criteria) {
            query.addCriteria(criteria);
        }
        return query;
    }

    protected Query genQuery(Map<String, PropertyInfo> propertyInfos, SearchParam param, String prefix
            , List<Criteria> criterias, Function<PrefixFunction, String> resetPrefix) {
        Query query = new Query();

        Criteria criteria = genCriteria(propertyInfos, param, prefix, criterias, resetPrefix);
        if (null != criteria) {
            query.addCriteria(criteria);
        }
        return query;
    }

    protected Criteria genCriteria(SearchParam searchParam) {
        return genCriteria(searchParam, false);
    }

    /*
     * searchParam: 查询参数
     * genCriteriaBySubcondition: 是否直接根据subCondition来生成查询信息
     * 并不通过属性类型转换
     */
    protected Criteria genCriteria(SearchParam searchParam, boolean genCriteriaBySubcondition) {
        String operation = searchParam.getOperation();
        Criteria criteria = null;
        if (StringUtils.hasText(operation) && null != searchParam.getSearchParamList()) {
            CriteriaOpsType type = CriteriaOpsType.valueOf(operation);
            List<Criteria> criterias = new ArrayList<>();
            for (SearchParam subSearchParam : searchParam.getSearchParamList()) {
                Criteria subCriteria = genCriteria(subSearchParam, genCriteriaBySubcondition);
                if (null != subCriteria) {
                    criterias.add(subCriteria);
                }
            }
            criteria = genGroupOperation(type, criterias);

        } else if (genCriteriaBySubcondition) {
            List<Criteria> criterias = new ArrayList<>();
            if (null != searchParam.getCondition()) {
                for (Map.Entry<String, SubCondition> entry : searchParam.getCondition().entrySet()) {
                    String key = entry.getKey();
                    SubCondition subCondition = entry.getValue();
                    Criteria subCriteria = PropertyTypeManage.doCriteria(CriteriaOpsType.valueOf(subCondition.getOperation())
                            , key, subCondition.getValue());
                    if (null != subCriteria) {
                        criterias.add(subCriteria);
                    }

                }
                CriteriaOpsType ope = StringUtils.hasText(searchParam.getOperation()) ? CriteriaOpsType.valueOf(searchParam.getOperation()) : CriteriaOpsType.and;
                criteria = genGroupOperation(ope, criterias);
            }

        } else {
            Map<String, PropertyInfo> propertyInfos = null;
            if (null == searchParam.getSearchClass()) {
                T dataObject = genObject();
                propertyInfos = CMDBTool.genSearchPropertyInfo(dataObject.getClass());
            } else {
                propertyInfos = CMDBTool.genSearchPropertyInfo(searchParam.getSearchClass());
            }

            criteria = genCriteria(propertyInfos, searchParam, null);
        }

        return criteria;
    }

    private Criteria genGroupOperation(CriteriaOpsType type, List<Criteria> criterias) {
        Criteria criteria = null;
        if (null != criterias && !criterias.isEmpty()) {
            switch (type) {
                case and:
                    criteria = new Criteria().andOperator(criterias);
                    break;
                case or:
                    criteria = new Criteria().orOperator(criterias);
                    break;
            }
        }

        return criteria;
    }

    protected Criteria genCriteria(Map<String, PropertyInfo> propertyInfos, SearchParam param, List<Criteria> criterias) {
        return genCriteria(propertyInfos, param, null, criterias, null);
    }

    /*
     * propertyInfos: 属性列表
     * param: 查询参数
     * criterias: 已经生成的查询条件
     * 例如: 实例信息会把动态定义的字段放在data map中, 内部的一边信息作为类的成员变量
     * 此时criterias就可以先通过反射来生成类成员变量的查询条件,此时不需要加前缀
     * 然后再和需要加前缀的动态定义字段组合到一起
     */
    protected Criteria genCriteria(Map<String, PropertyInfo> propertyInfos, SearchParam param, String prefix
            , List<Criteria> criterias, Function<PrefixFunction, String> resetPrefix) {
        String operation = param.getOperation();
        CriteriaOpsType type = null != operation ? CriteriaOpsType.valueOf(operation) : CriteriaOpsType.and;
        List<Criteria> criteriaList = null;
        if (null != criterias) {
            criteriaList = criterias;
        } else {
            criteriaList = new ArrayList<>();
        }
        List<Criteria> criteriaFlagList = new ArrayList<>();
        Map<String, SubCondition> map = param.getCondition();
        //实例instanceService传入的参数
        Map<String, Object> paramMap = param.getParam();
        MwUserApi mwUserApi = null;
        if (paramMap != null && paramMap.containsKey(ViewConvertContext.MwuserApiKey)) {
            mwUserApi = (MwUserApi) paramMap.get(ViewConvertContext.MwuserApiKey);
        }

        if (null != map) {
            for (String key : map.keySet()) {
                PropertyInfo propertyInfo = propertyInfos.get(key);
                if (null != propertyInfo) {
                    PropertyType propertyType = propertyInfo.getPropertyType();
                    if (propertyType!=null) {
                        if(mwUserApi != null){
                            propertyType.init(mwUserApi);
                        }
                        //有的字段需要加前缀,有的字段不需要加前缀
                        String prefixReset = null;
                        if (null != resetPrefix) {
                            PrefixFunction prefixFunction = new PrefixFunction(prefix, propertyInfo.getName());
                            prefixReset = resetPrefix.apply(prefixFunction);
                        }

                        Criteria criteria = propertyType.criteria(propertyInfo, map, prefixReset);
                        if (null != criteria) {
                            criteriaList.add(criteria);
                            criteriaFlagList.add(criteria);
                        }
                    }

                }
            }
        }
        Criteria ret = new Criteria();
        switch (type) {
            case and:
                ret = new Criteria().andOperator(criteriaList);
                break;
            case or:
                ret = new Criteria().orOperator(criteriaList);
                break;
            case keyword:
                if (map != null) {//判断condition是否有查询条件
                    //Condition中的查询条件，使用and连接
                    if (CollectionUtils.isNotEmpty(criteriaList)) {
                        ret = new Criteria().andOperator(criteriaList);
                        //去除Condition中的查询条件，避免查询条件覆盖导致无效查询
                        criteriaList.removeAll(criteriaFlagList);
                    }
                }
                keywordCriteria(param.getKeywordValue(), propertyInfos, criteriaList, prefix);
                ret = ret.orOperator(criteriaList);
        }
        if (criteriaList.size() == 0) {
            return null;
        }

        return ret;
    }

    protected Criteria genCriteriaNotByProperty(Map<String, PropertyInfo> propertyInfos, SearchParam param, String prefix
            , List<Criteria> criterias, Function<PrefixFunction, String> resetPrefix) {
        String operation = param.getOperation();
        CriteriaOpsType type = null != operation ? CriteriaOpsType.valueOf(operation) : CriteriaOpsType.and;

        List<Criteria> criteriaList = null;
        if (null != criterias) {
            criteriaList = criterias;
        } else {
            criteriaList = new ArrayList<>();
        }
        Map<String, SubCondition> map = param.getCondition();
        if (null != map) {

            for (String key : map.keySet()) {
                PropertyInfo propertyInfo = propertyInfos.get(key);
                if (null != propertyInfo) {
                    PropertyType propertyType = propertyInfo.getPropertyType();

                    //有的字段需要加前缀,有的字段不需要加前缀
                    String prefixReset = null;
                    if (null != resetPrefix) {
                        PrefixFunction prefixFunction = new PrefixFunction(prefix, propertyInfo.getName());
                        prefixReset = resetPrefix.apply(prefixFunction);
                    }

                    Criteria criteria = propertyType.criteria(propertyInfo, map, prefixReset);
                    if (null != criteria) {
                        criteriaList.add(criteria);
                    }
                }
            }

//            for (String key : map.keySet()) {
//                SubCondition subCondition = map.get(key);
//                Object value = subCondition.getValue();
//                //有的字段需要加前缀,有的字段不需要加前缀
//                String prefixReset = null;
//                if (null != resetPrefix) {
//                    PrefixFunction prefixFunction = new PrefixFunction(prefix, key);
//                    prefixReset = resetPrefix.apply(prefixFunction);
//                }
//                if (!Strings.isNullOrEmpty(prefixReset)) {
//                    PrefixFunction prefixFunction = new PrefixFunction(prefix, key);
//                    prefixReset = resetPrefix.apply(prefixFunction);
//                    String name = !StringUtils.hasText(prefixReset) ? key : formatSearchKey(prefixReset, key);
//                    Pattern pattern = Pattern.compile(value.toString(), Pattern.CASE_INSENSITIVE);
//                    Criteria criteria = new Criteria(name).regex(pattern);
//                    if (null != criteria) {
//                        criteriaList.add(criteria);
//                    }
//                } else {
//                    PropertyInfo propertyInfo = propertyInfos.get(key);
//                    if (null != propertyInfo) {
//                        PropertyType propertyType = propertyInfo.getPropertyType();
//
//                        if (null != resetPrefix) {
//                            PrefixFunction prefixFunction = new PrefixFunction(prefix, propertyInfo.getName());
//                            prefixReset = resetPrefix.apply(prefixFunction);
//                        }
//
//                        Criteria criteria = propertyType.criteria(propertyInfo, map, prefixReset);
//                        if (null != criteria) {
//                            criteriaList.add(criteria);
//                        }
//                    }
//                }
//            }
        }
        Criteria ret = null;
        switch (type) {
            case and:
                ret = new Criteria().andOperator(criteriaList);
                break;
            case or:
                ret = new Criteria().orOperator(criteriaList);
                break;
            case keyword:
                keywordCriteria(param.getKeywordValue(), propertyInfos, criteriaList, prefix);
                ret = new Criteria().orOperator(criteriaList);
        }

        if (criteriaList.size() == 0) {
            return null;
        }

        return ret;
    }

    //所有字符串类型的字段生成like查询
    protected void keywordCriteria(String keyword, Map<String, PropertyInfo> propertyInfos, List<Criteria> criteriaList, String prefix) {
        for (PropertyInfo propertyInfo : propertyInfos.values()) {
            PropertyType propertyType = propertyInfo.getPropertyType();
            if (propertyInfo.isKeywordSearch()) {
                Map<String, SubCondition> condition = new HashMap<>();
                SubCondition subCondition = new SubCondition(CriteriaOpsType.like.name(), keyword);
                condition.put(propertyInfo.getName(), subCondition);

                Criteria criteria = propertyType.criteria(propertyInfo, condition, prefix);
                if (null != criteria) {
                    criteriaList.add(criteria);
                }
            }

        }
    }

    //根据id集合生成查询
    protected Query genQueryByIds(List ids) {
        T dataObject = genObject();
        return CMDBTool.genInIdsQuery(dataObject.getClass(), ids);
    }
}
